package io.gitee.kotle.android.utils

import android.app.Activity
import android.os.Build
import android.view.View
import android.view.Window
import android.view.WindowManager
import android.widget.EditText
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsAnimationCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import io.gitee.kotle.android.iInterface.ILifecycleObserver

/**
 * 监听键盘高度变化，兼容安卓11以下机型
 * 安卓11以下，不支持控制键盘移动
 * 对话框使用，需要传入activity的window对象，如果传入dialog的window对象，会导致监听无效
 */
/**
 * 是否支持键盘动画，安卓11及以上才支持
 */
val supportImeAnim
    get() = Build.VERSION.SDK_INT >= 30

/**
 * 显示键盘.
 * 如果view是editText，可以显示键盘，并且这个editText获取焦点
 * 其他view，如果当前有其他editText有焦点，则能弹出，当前的焦点view不会发生变化，如果没有焦点view，怎不会弹出键盘
 */
fun Window.showIme(editText: EditText) {
    editText.isFocusableInTouchMode = true
    editText.requestFocus()
    WindowCompat.getInsetsController(this, editText).show(WindowInsetsCompat.Type.ime())
}

/**
 * 隐藏键盘，
 * 任意view均可隐藏
 */
fun Window.hideIme() {
    WindowCompat.getInsetsController(this, decorView).hide(WindowInsetsCompat.Type.ime())
}

/**
 * 隐藏键盘
 */
fun Activity.hideIme() = window.hideIme()

/**
 * 显示键盘
 */
fun Activity.showIme(editText: EditText) = window.showIme(editText)

/**
 * 键盘是否可见
 */
fun View.imeVisible(): Boolean? {
    return ViewCompat.getRootWindowInsets(this)?.isVisible(WindowInsetsCompat.Type.ime())
}

/**
 * ime是否可见
 */
fun Activity.imeVisible(): Boolean? {
    return window.decorView.imeVisible()
}

/**
 * ime是否可见
 */
fun Fragment.imeVisible(): Boolean? {
    return activity?.window?.decorView?.imeVisible()
}

open class ImeHelper(
    private val window: Window,
    private val support: Boolean = supportImeAnim/*是否只支持高版本*/,
    private val autoSetSoftInputMode: Boolean = supportImeAnim/*是否根据不同情况，自动调节输入法模式*/,
    @DispatchMode dispatchMode: Int = DISPATCH_MODE_CONTINUE_ON_SUBTREE
) : WindowInsetsAnimationCompat.Callback(dispatchMode), ILifecycleObserver {
    companion object {
        operator fun invoke(
            fragment: Fragment,
            support: Boolean = supportImeAnim,
            autoSetSoftInputMode: Boolean = supportImeAnim
        ): ImeHelper {
            val imeHelper =
                ImeHelper(fragment.appCompatActivity.window, support, autoSetSoftInputMode)
            fragment.viewLifecycleOwner.lifecycle.addObserver(imeHelper)
            return imeHelper
        }

        operator fun invoke(
            activity: AppCompatActivity,
            support: Boolean = supportImeAnim,
            autoSetSoftInputMode: Boolean = supportImeAnim,/*是否监听窗口变化*/
        ): ImeHelper {
            val imeHelper = ImeHelper(activity.window, support, autoSetSoftInputMode)
            activity.lifecycle.addObserver(imeHelper)
            return imeHelper
        }
    }

    val decorView
        get() = window.decorView

    init {
        if (support) {
            onInitIme()
        }
    }

    private fun onInitIme() {
        if (autoSetSoftInputMode) {
            val inputModel = if (supportImeAnim) {
                WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING
            } else {
                WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
            }
            window.setSoftInputMode(inputModel)
        }
        ViewCompat.setWindowInsetsAnimationCallback(decorView, this)
    }

    /**
     * 隐藏键盘的监听
     */
    private var hideImeListener: (() -> Unit)? = null

    /**
     * 当可见时，是否自动更新键盘状态
     */
    var isAutoUpdateImeStatus = true

    /**
     * 键盘动画是否正在运行
     */
    private var _animIsRunning = false
    val isImeAnimRunning
        get() = _animIsRunning

    /**
     * 键盘的最大值
     */
    private var _imeMaxHeight = -1
    val imeMaxHeight
        get() = _imeMaxHeight

    /**
     * 状态栏高度
     */
    private var _statusBarHeight: Int = -1
    val statusBarHeight
        get() = _statusBarHeight

    /**
     * 导航栏高度
     */
    private var _navigationBarHeight: Int = -1
    val navigationBarHeight
        get() = _navigationBarHeight

    override fun onProgress(
        insets: WindowInsetsCompat,
        runningAnimations: MutableList<WindowInsetsAnimationCompat>
    ): WindowInsetsCompat {
        return updateListener(insets, runningAnimations)
    }

    override fun onPrepare(animation: WindowInsetsAnimationCompat) {
        //如果这里回调可以走，那么onProgress回调就会走
        _animIsRunning = true
        super.onPrepare(animation)
    }

    override fun onEnd(animation: WindowInsetsAnimationCompat) {
        super.onEnd(animation)
        _animIsRunning = false
        imeAnimEnd()
    }

    override fun onStart(
        animation: WindowInsetsAnimationCompat,
        bounds: WindowInsetsAnimationCompat.BoundsCompat
    ): WindowInsetsAnimationCompat.BoundsCompat {
        //因为键盘是屏幕最下面弹出来，最终是在导航栏上方
        //所以这里的[insets.getInsets(WindowInsetsCompat.Type.ime())]获取高度，如果有导航栏，则是导航栏+真实键盘的高度
        //如果导航栏不存在，[insets.getInsets(WindowInsetsCompat.Type.ime())]获取的就是键盘真实高度
        _imeMaxHeight = bounds.upperBound.bottom
        return super<WindowInsetsAnimationCompat.Callback>.onStart(animation, bounds)
    }


    /**
     * 键盘变化监听器
     */
    private var imeChangeListener: Function4<Int, Int, Int, Int, Unit>? = null

    /**
     * 更新监听
     */
    private fun updateListener(
        insets: WindowInsetsCompat,
        runningAnimations: MutableList<WindowInsetsAnimationCompat>
    ): WindowInsetsCompat {
        val imeMaxHeight = this.imeMaxHeight
        if (imeMaxHeight <= 0) {
            return insets
        }
        //键盘高度
        val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
        if (imeMaxHeight <= 0) {
            return insets
        }
        //状态栏高度
        _statusBarHeight = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top
        //导航栏高度
        _navigationBarHeight = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom
        imeChangeListener?.invoke(imeHeight, imeMaxHeight, statusBarHeight, navigationBarHeight)
        return onUpdateListener(
            imeHeight,
            imeMaxHeight,
            statusBarHeight,
            navigationBarHeight,
            insets,
            runningAnimations
        )
    }


    /**
     * 更新监听
     * [imeHeight] 键盘实时高度
     * [imeMaxHeight] 键盘最大
     * [statusBarHeight] 状态栏高度
     * [navigationBarHeight] 导航栏高度
     */
    open fun onUpdateListener(
        imeHeight: Int,
        imeMaxHeight: Int,
        statusBarHeight: Int,
        navigationBarHeight: Int,
        insets: WindowInsetsCompat,
        runningAnimations: MutableList<WindowInsetsAnimationCompat>
    ): WindowInsetsCompat {
        return WindowInsetsCompat.CONSUMED
    }

    override fun onStart() {
        super<ILifecycleObserver>.onStart()
        if (isAutoUpdateImeStatus) {
            updateImeStatus()
        }
    }


    /**
     * 设置监听
     */
    fun setOnImeListener(
        listener: ((
            imeOffset: Int,
            imeMaxHeight: Int/*包含导航栏和状态栏总高度*/,
            statusBarHeight: Int,
            navigationBarHeight: Int
        ) -> Unit)?
    ) {
        this.imeChangeListener = listener
    }

    override fun onDestroy() {
        super.onDestroy()
        this.imeChangeListener = null
        ViewCompat.setWindowInsetsAnimationCallback(decorView, null)
    }

    private fun imeAnimEnd() {
        hideImeListener?.invoke()
        hideImeListener = null
    }

    /**
     * 隐藏键盘
     */
    fun hideIme(block: (() -> Unit)? = null) {
        if (window.decorView.imeVisible() == true) {
            hideImeListener = block
            window.hideIme()
        } else {
            block?.invoke()
        }
    }

    /**
     * 更新键盘状态
     */
    fun updateImeStatus() {
        //键盘动画正在执行
        if (isImeAnimRunning) {
            return
        }
        val imeMaxHeight = this.imeMaxHeight
        //键盘没有高度
        if (imeMaxHeight <= 0) {
            return
        }
        val imeShow = decorView.imeVisible() ?: return
        val statusBarHeight = this.statusBarHeight
        val navigationBarHeight = this.navigationBarHeight
        val imeHeight = if (imeShow) {
            imeMaxHeight
        } else {
            0
        }
        imeChangeListener?.invoke(imeHeight, imeMaxHeight, statusBarHeight, navigationBarHeight)
    }
}